<?php
/**
 * Created by PhpStorm.
 * User: chenguangpeng
 * Date: 8/12-012
 * Time: 9:48
 * 民生付
 */

namespace Business\CMBC;
use Exception;

class ToolBox
{
    //const LAJP_IP         = "10.160.4.140";            //Java端IP
    const LAJP_IP           = "localhost";            //Java端IP
    const LAJP_PORT         = 21230;                //Java端侦听端口
    const PARAM_TYPE_ERROR  = 101;              //参数类型错误
    const SOCKET_ERROR      = 102;            //SOCKET错误
    const JAVA_EXCEPTION    = 104;            //Java端反馈异常

    private $api_code_list = [
        '90000000'=> '成功',
        '6002020N'=> '第N个参数为空',
        '60020210'=> 'base64编码异常',
        '60020211'=> 'base64解码异常',
        '60020212'=> 'PFX密码错误',
        '60020213'=> 'x509初始化错误',
        '60020217'=> '不支持的签封算法',
        '60020290'=> '工具包错误',
        '60020291'=> '未知错误',
    ];

    public function __construct()
    {
    }
    public function __destruct()
    {
    }

    public static function lajp_call()
    {
        //参数数量
        $args_len = func_num_args();
        //参数数组
        $arg_array = func_get_args();
        //参数数量不能小于1
        if ($args_len < 1)  {
            throw new Exception("[LAJP Error] lajp_call function's arguments length < 1", self::PARAM_TYPE_ERROR);
        }
        //第一个参数是Java类、方法名称，必须是string类型
        if (!is_string($arg_array[0])) {
            throw new Exception("[LAJP Error] lajp_call function's first argument must be string \"class_name::method_name\".", self::PARAM_TYPE_ERROR);
        }
        if (($socket = socket_create(AF_INET, SOCK_STREAM, 0)) === false) {
            throw new Exception("[LAJP Error] socket create error.",  self::SOCKET_ERROR);
        }
        if (socket_connect($socket, self::LAJP_IP, self::LAJP_PORT) === false) {
            throw new Exception("[LAJP Error] socket connect error.",  self::SOCKET_ERROR);
        }
        //消息体序列化
        $request = serialize($arg_array);
        $req_len = strlen($request);
        $request = $req_len.",".$request;
        $send_len = 0;
        do {
            //发送
            if (($sends = socket_write($socket, $request, strlen($request))) === false) {
                throw new Exception("[LAJP Error] socket write error.",  self::SOCKET_ERROR);
            }
            $send_len += $sends;
            $request = substr($request, $sends);
        } while ($send_len < $req_len);

        //接收
        $response = "";
        while(true) {
            $recv = "";
            if (($recv = socket_read($socket, 1400)) === false) {
                throw new Exception("[LAJP Error] socket read error.",  self::SOCKET_ERROR);
            }
            if ($recv == "") {
                break;
            }
            $response .= $recv;
        }
        //关闭
        socket_close($socket);
        $rsp_stat = substr($response, 0, 1);     //返回类型 "S":成功 "F":异常
        $rsp_msg = substr($response, 1);        //返回信息
        //echo "返回类型:{$rsp_stat},返回信息:{$rsp_msg}<br>";
        if ($rsp_stat == "F") {
            //异常信息不用反序列化
            throw new Exception("[LAJP Error] Receive Java exception: ".$rsp_msg, self::JAVA_EXCEPTION);
        }
        else {
            if ($rsp_msg != "N") { //返回非void
                //反序列化
                return unserialize($rsp_msg);
            }
        }
    }

    /**
     * 获取毫秒数
     *
     * @return float
     */
    public function getMillisecond()
    {
        list($usec, $sec) = explode(" ", microtime());
        $msec=round($usec*1000);
        return $msec;
    }
    /**
     * 获取消息签名
     *
     * @param string $base64SourceData 原文字符串
     * @param string $private_cert_path 证书路径[P12内容,私钥证书内容]
     * @param string $private_cert_pwd P12密码[私钥证书密码]
     * @param string $signAlg 签名算法，可选项：SHA1withRSAEncryption, SHA256withRSAEncryption,SHA512withRSA, SM3withSM2
     * @return array
     */
    public function getSign($base64SourceData, $private_cert_path, $private_cert_pwd,$signAlg='SM3withSM2')
    {
        if (!file_exists($private_cert_path)) {
            return ['return_code'=>0, 'return_msg'=>'获取消息签名失败，私钥证书不存在' . $private_cert_path];
        }
        $P12Data          = file_get_contents($private_cert_path);
        $base64P12Data    = base64_encode($P12Data);
        $ret = self::lajp_call("cfca.sadk.api.SignatureKit::P1SignMessage",$signAlg, $base64SourceData, $base64P12Data,$private_cert_pwd);
        $res = json_decode($ret, true);
        if ($res['Code']=='90000000') {
            return ['return_code'=>'SUCCESS', 'data'=>$res['Base64SignatureData'], 'return_msg'=>'success'];
        }
        return ['return_code'=>0,'return_msg'=>"获取消息签名失败,错误代码:{$res['Code']},错误描述:{$this->api_code_list[$res['Code']]}"];
    }

    /**
     * 签名验证-校验p1签名
     *
     * @param string $sourceData base64格式原文[方法getWxPaySign=>base64_encode($sourceData)字段]
     * @param string $public_cert_path 商户公钥证书路径
     * @param string $sign base64格式P1签名内容[通过方法getWxPaySign得到的签名]
     * @param string $signAlg 签名算法
     * @return array
     */
    public function checkSign($sourceData, $public_cert_path, $sign,$signAlg='SM3withSM2')
    {
        if (!file_exists($public_cert_path)) {
            return ['Code'=>0, 'return_msg'=>'签名验证失败，银行公钥证书不存在'];
        }
        $bank_cert = base64_encode(file_get_contents($public_cert_path));
        $ret = self::lajp_call("cfca.sadk.api.SignatureKit::P1VerifyMessage",$signAlg, $sourceData, $bank_cert,$sign);
        $res = json_decode($ret, true);
        if ($res['Code']=='90000000') {
            if ($res['Result']=="True") return ['return_code'=>'SUCCESS', 'data'=>$res['Result'], 'return_msg'=>'success'];
            return ['return_code'=>0,'return_msg'=>'fail'];
        }
        return ['return_code'=>0,'return_msg'=>"签名验证失败,错误代码:{$res['Code']},错误描述:{$this->api_code_list[$res['Code']]}"];
    }

    /**
     * 生成数字信封
     *
     * @param string $content 要加密的数据原文
     * @param string $public_cert_path  商户公钥证书路径
     * @param string $signAlg 对称加密算法，可选项：SM4/CBC/PKCS7Padding,DESede/CBC/PKCS7PaddingRC4,
     * @return array ['Base64EnvelopeMessage']=>'','Code'=>]
     */
    public function envelopeMessage($content, $public_cert_path, $signAlg='SM4/CBC/PKCS7Padding')
    {
        if (!file_exists($public_cert_path)) {
            return ['return_code'=>0, 'return_msg'=>'生成数字信封失败，公钥证书不存在' . $public_cert_path];
        }
        $base64SourceData = base64_encode($content);
        $bank_cert        = base64_encode(file_get_contents($public_cert_path));
        $ret = self::lajp_call("cfca.sadk.api.EnvelopeKit::envelopeMessage", $base64SourceData, $signAlg, $bank_cert);
        $res = json_decode($ret, true);
        if ($res['Code']=='90000000') {
            return ['return_code'=>'SUCCESS', 'data'=>$res['Base64EnvelopeMessage'], 'return_msg'=>'success'];
        }
        return ['return_code'=>0,'return_msg'=>"生成数字信封失败，错误代码:{$res['Code']},错误描述:{$this->api_code_list[$res['Code']]}"];
    }

    /**
     * 打开数字信封
     *
     * @param string $base64EnvelopeData 需要解密的数据，base64格式的字符串
     * @param string $private_cert_path 商户私钥证书路径
     * @param string $private_cert_pwd 商户私钥证书密码
     * @param string $signAlg 对称加密算法，可选项：SM4/CBC/PKCS7Padding,DESede/CBC/PKCS7PaddingRC4,
     * @return array ["return_code"=>"","data"=>"","return_msg"=>'']
     */
    public function openMessage($base64EnvelopeData, $private_cert_path, $private_cert_pwd, $signAlg='SM4/CBC/PKCS7Padding')
    {
        if (!file_exists($private_cert_path)) {
            return ['return_code'=>0, 'return_msg'=>'打开数字信封失败，私钥证书不存在'];
        }
        $private_cert = base64_encode(file_get_contents($private_cert_path));
        $ret = self::lajp_call("cfca.sadk.api.EnvelopeKit::openEvelopedMessage",  $base64EnvelopeData,$signAlg, $private_cert, $private_cert_pwd);
        $res = json_decode($ret, true);
        if ($res['Code']=='90000000') {
            return ['return_code'=>'SUCCESS', 'data'=>$res['Base64SourceString'], 'return_msg'=>'success'];
        }
        return ['return_code'=>0,'return_msg'=>$ret."打开数字信封失败，错误代码:{$res['Code']},错误描述:{$this->api_code_list[$res['Code']]}"];
    }
}